{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# BP神经网络\n",
    "### BP神经网络也叫做误差反向传递网络\n",
    "> 引用 https://blog.csdn.net/daaikuaichuan/article/details/81135802\n",
    "### BP神经网络基本原理图：\n",
    "![image-2.png](https://img-blog.csdn.net/20180720162033746?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhYWlrdWFpY2h1YW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### BP神经网络分为输入层，隐含层，以及输出层。如上图：\n",
    "\n",
    "- 对于输出层节点i：\n",
    "  $δ_i = y_i(1-y_i)(t_i-y_i)$\n",
    "- 对于隐藏层节点$δ_i$:\n",
    "  $δ_i = a_i(1-a_i) \\sum_{k=1}^n W_{ki}*δ_k$\n",
    "最后，更新每个连接上的权值：\n",
    "- $W_{ji} \\leftarrow W_{ji} + η * δ_j * X_{ji}$\n",
    "其中η为学习率(学习率过大和过小都不适合拟合)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### BP神经网络推导基本过程：\n",
    "1. 我们取网络所有输出层节点的误差平方和作为目标函数：\n",
    "- $Error = 1/2 * \\sum (t_i - y_i)^2$\n",
    "\n",
    "误差平方和越小，说明拟合度越高，越接近于真实情况。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2. 求出误差后，我们需要将误差反向传递，修改每个节点间的权重，我们运用随机梯度下降算法对目标函数进行优化：\n",
    "\n",
    "- $W_{ji} \\leftarrow W_{ji}−η * \\partial E_d / \\partial W_{ji}$\n",
    "\n",
    "3. 根据链式求导法则，可以得到：\n",
    "-  $\\partial E_d / \\partial W_{ji} = \\partial E_d / \\partial net_j * \\partial net_j / \\partial W_{ji}$\n",
    "-  $\\partial E_d / \\partial W_{ji} = \\partial E_d / \\partial net_j * \\sum_{j=1}^n \\partial W_{ji} * X_{ji} / \\partial W_{ji}$\n",
    "-  $\\partial E_d / \\partial W_{ji} = \\partial E_d / \\partial net_j * X_{ji}$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 这里权重的推导需要区分从输出层到隐含层的权重和隐含层到输入层的权重，对应链式求导有不同。\n",
    "4. 对于输出层到隐含层：\n",
    "- $\\partial E_d / \\partial net_j = \\partial E_d / \\partial y_j * \\partial y_j / \\partial net_{ji}$\n",
    "其中$y_j$为$net_j$的函数，即$y_j=sigmoid(net_j)$，并且有$y_j$的一阶导数为：\n",
    "- $y_j^{-1} = y_j·(1-y_j)$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "5. 对于隐含层到输入层：\n",
    "- $\\partial E_d / \\partial net_j = \\sum_{k=1}^n \\partial E_d / \\partial net_k * \\partial net_k / \\partial net_j$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上就是神经网络的大概流程：输入→隐含层→输出→计算误差(均方差)→误差函数反向传递→计算各层之间的权重更新值（运用梯度下降）→反向传递，如此重复训练，设定误差范围，最终能够找到最优的拟合或分割曲线。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# BP神经网络实现Iris数据集分类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "error= 0.05078944557048786\n",
      "测试分数为: 0.9333333333333333\n"
     ]
    },
    {
     "data": {
      "image/png": "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lFyJiIhIUtqypT2O4494TElJGrt358UpIpeSKxEREUlKHo+/RlcHer2RE7BYU3IlIiIiSal375VY64lwhCUvbzd5ebvjFhPoakERSYBp00yiQxCpE7VfaBg6dNhGt25rWb++S5gky3DccTNx4jyUpJErERERSVoXXvg/2rXbDoAxgeCf7jTgccd9wbBh38Q9Jo1ciYiISNLKyjrENdc8zsqVvVmypD9FRWm0bLmHYcPm0abNzoTEpORKREREkprjWPr0WUGfPisSHQqgaUERERGRmFJyJSIiIhJDmhYUERGpAV0hKDWlkSsRERGRGFJyJSIiIhJDSq5EREREYkjJlYiIiEgMKbkSERERiSFdLSgiIhKBrhKU2tLIlYiIiEgMKbkSERERiSElVyIiIiIxpORKREREJIZU0C4icTFtmkl0CCI1piJ2qQuNXImIiIjEUEyTK2NMK2PMWmNMtxoeP84Ys8wYs9MYc2ssYxERERFJhJglV8aYVsC7QLcaHt8aeBt4ERgDXGKMmRCreEREREQSIZYjVy8BL9Ti+EuALcAfrbUrgT8AV8UwHhEREZG4i2VydY219p+1OH4wMM1aa4P3ZwPDwx1sjEkzxuSU3YDsOsQqIiIiUi9illxZa9fW8iE5QMXH7AM6RDj+dqCgwm1TLZ9PREREpN4l8mpBH1Bc4X4RkBnh+HuA3Aq3TvUXmoiIiMiRSWSfq91A6wr3s4GScAdba4upkIwZo545IiISO+ptJbGSyJGrObhXCZYZCmxOUCwiIiIiMVHvyVWwAD0lxK63gWONMScH9/8S+LC+4xERERGpT/EYuVoETKq60Vq7E7gFeA/YDvQB/hSHeERERETqTcxrrqy1psr9bhGO/bcx5kOgL/CFtfZArOMRERERiaeEL9wcbOFQ2zYOIiIiIg1SwpMrERGRRNJVghJribxaUERERKTR0ciViNSradPUk05EmhaNXImIiIjEkEaumpgObOYqnmAASygkkzc5h3eZjF//FERERGJCv1GbkGv4D4/wE8BigAAOl/MM39KPU/mILXRMdIgiInGhInapT5oWbCJO5UP+w3V48eMlgIcAKfgAOIoVfMDpGAIJjlJERCT5KblqIm7nHnx4Qu5LwcdAlnAKH8c5KhERkcZHyVUTkMUBxvMZXvxhjynBy1m8HceoREREGiclV01AKiVRjzFA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\n",
      "text/plain": [
       "<Figure size 700x500 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import random\n",
    "import math\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib as mpl\n",
    "from sklearn.decomposition import PCA\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "\n",
    "# 定义类别转换函数\n",
    "def trtype(s):\n",
    "    types = {b'Iris-setosa': 0, b'Iris-versicolor': 1, b'Iris-virginica': 2}\n",
    "    return types[s]\n",
    "\n",
    "\n",
    "# 划分测试集和训练集\n",
    "def label_tr(input_label):  # 标签转换，将一维标签转换为三维\n",
    "    label_list = {0: [1, 0, 0], 1: [0, 1, 0], 2: [0, 0, 1]}\n",
    "    label_change = []\n",
    "    for i in range(len(input_label)):\n",
    "        label_change.append(label_list[int(input_label[i])])\n",
    "    return np.array(label_change)\n",
    "\n",
    "\n",
    "def inv_label_tr(input_label_change):  # 标签转换逆过程\n",
    "    y_real = []\n",
    "    for i in range(input_label_change.shape[0]):\n",
    "        for j in range(3):\n",
    "            if input_label_change[i][j] == 1:\n",
    "                y_lable = j\n",
    "        y_real.append(y_lable)\n",
    "    return np.array(y_real)\n",
    "\n",
    "\n",
    "def rand(a, b):  # 随机数函数 ---> 生成 a - b 之间的随机数\n",
    "    return (b - a) * random.random() + a   # random.random()用于生成随机的 0 - 1之间的浮点数\n",
    "\n",
    "\n",
    "def make_matrix(m, n, fill=0.0):  # 矩阵生成函数\n",
    "    mat = []\n",
    "    for i in range(m):\n",
    "        mat.append([fill] * n)\n",
    "    return mat    # 生成 m x n 的零矩阵\n",
    "\n",
    "\n",
    "def sigmoid(x):  # 激活函数\n",
    "    return 1.0 / (1.0 + math.exp(-x))\n",
    "\n",
    "\n",
    "def sigmoid_derivative(x):  # 激活函数求导\n",
    "    return x * (1 - x)\n",
    "\n",
    "\n",
    "class BPNeuralNetwork:  # BP神经网络类\n",
    "    def __init__(self):  # 初始化\n",
    "        self.input_n = 0   # 输入层\n",
    "        self.hidden_n = 0  # 隐含层\n",
    "        self.output_n = 0  # 输出层\n",
    "        self.input_cells = []  # 输入层节点数\n",
    "        self.hidden_cells = []  # 隐含层节点数\n",
    "        self.output_cells = []  # 输出层节点数\n",
    "        self.input_weights = []  # 输入层-->隐含层间的权重\n",
    "        self.output_weights = []  # 隐含层-->输出层间的权重\n",
    "        self.input_correction = []\n",
    "        self.output_correction = []\n",
    "\n",
    "        \n",
    "    def setup(self, n_in, n_hidden, n_out):   # 输入 2 5 3 \n",
    "        # 初始化输入、隐层、输出元数\n",
    "        self.input_n = n_in + 1  # n_in+1为输入层增加一个X0单元，代码中也加入了b值：a[1]=W.T*X+b\n",
    "        self.hidden_n = n_hidden\n",
    "        self.output_n = n_out\n",
    "        # 初始化神经元\n",
    "        self.input_cells = [1.0] * self.input_n   # 激活神经元\n",
    "        self.hidden_cells = [1.0] * self.hidden_n\n",
    "        self.output_cells = [1.0] * self.output_n\n",
    "        # 初始化权重矩阵\n",
    "        self.input_weights = make_matrix(self.input_n, self.hidden_n)  # 创建一个 input_n x hidden_n 的权重矩阵 3 X 5\n",
    "        self.output_weights = make_matrix(self.hidden_n, self.output_n)  # 5 X 3\n",
    "        # 初始化权重\n",
    "        for i in range(self.input_n):\n",
    "            for h in range(self.hidden_n):\n",
    "                self.input_weights[i][h] = rand(-0.2, 0.2)\n",
    "        for h in range(self.hidden_n):\n",
    "            for o in range(self.output_n):\n",
    "                self.output_weights[h][o] = rand(-2.0, 2.0)\n",
    "        # 初始化偏置\n",
    "        self.input_correction = make_matrix(self.input_n, self.hidden_n)\n",
    "        self.output_correction = make_matrix(self.hidden_n, self.output_n)\n",
    "\n",
    "        \n",
    "    def predict(self, inputs):   # inputs = x_train\n",
    "        #  激活输入层\n",
    "        for i in range(self.input_n - 1):  # i = 0 , 1 , 2\n",
    "            self.input_cells[i] = inputs[i]  # ----> input_cells = [x_test]\n",
    "        # 激活隐层\n",
    "        for j in range(self.hidden_n):\n",
    "            total = 0.0\n",
    "            for i in range(self.input_n):\n",
    "                total += self.input_cells[i] * self.input_weights[i][j]   # 神经元中是测试集的数据，乘以权重矩阵就是这一层的输入值\n",
    "            self.hidden_cells[j] = sigmoid(total)    # 隐层输出\n",
    "        # 激活输出层\n",
    "        for k in range(self.output_n):\n",
    "            total = 0.0\n",
    "            for j in range(self.hidden_n):\n",
    "                total += self.hidden_cells[j] * self.output_weights[j][k]  # 隐层输入 x 输入权重 = 输出层输入\n",
    "            self.output_cells[k] = sigmoid(total)    # 输出层输出\n",
    "        return self.output_cells[:]\n",
    "\n",
    "    \n",
    "    def back_propagate(self, x_train, label, lr, correct):  # cases = x_train[i]  label = y_train[i]\n",
    "        '''\n",
    "        反向传播算法\n",
    "        :param x_train: 训练数据\n",
    "        :param label: 标注\n",
    "        :param lr: 学习率\n",
    "        :param correct: 惯性动量系数\n",
    "        :return: 误差\n",
    "        '''\n",
    "        # 正向传播\n",
    "        self.predict(x_train)   # 得到的是输出层的输出\n",
    "        # 求输出误差\n",
    "        output_deltas = [0.0] * self.output_n  # [0, 0, 0]的数组\n",
    "        for o in range(self.output_n):\n",
    "            error = label[o] - self.output_cells[o]\n",
    "            output_deltas[o] = sigmoid_derivative(self.output_cells[o]) * error\n",
    "        # 求隐层误差\n",
    "        hidden_deltas = [0.0] * self.hidden_n\n",
    "        for h in range(self.hidden_n):\n",
    "            error = 0.0\n",
    "            for o in range(self.output_n):\n",
    "                error += output_deltas[o] * self.output_weights[h][o]\n",
    "            hidden_deltas[h] = sigmoid_derivative(self.hidden_cells[h]) * error\n",
    "        # 更新输出权重\n",
    "        for h in range(self.hidden_n):\n",
    "            for o in range(self.output_n):\n",
    "                change = output_deltas[o] * self.hidden_cells[h]\n",
    "                self.output_weights[h][o] += lr * change + correct * self.output_correction[h][o]\n",
    "                self.output_correction[h][o] = change\n",
    "        # 更新输入权重\n",
    "        for i in range(self.input_n):\n",
    "            for h in range(self.hidden_n):\n",
    "                change = hidden_deltas[h] * self.input_cells[i]\n",
    "                self.input_weights[i][h] += lr * change + correct * self.input_correction[i][h]\n",
    "                self.input_correction[i][h] = change\n",
    "        # 求全局误差\n",
    "        error = 0.0\n",
    "        for o in range(len(label)):\n",
    "            error += 0.5 * (label[o] - self.output_cells[o]) ** 2\n",
    "        return error\n",
    "\n",
    "    \n",
    "    def train(self, X_train, labels, epoch=10000, lr=0.05, correct=0.1):   # labels = y_train\n",
    "        # 训练神经网络\n",
    "        for j in range(epoch):\n",
    "            error = 0.0\n",
    "            for i in range(len(X_train)):\n",
    "                label = labels[i]\n",
    "                x_train = X_train[i]\n",
    "                error += self.back_propagate(x_train, label, lr, correct)\n",
    "        print(f\"error= {error}\")\n",
    "\n",
    "        \n",
    "    def fit(self, x_test):  # 离散预测函数用于输出数据\n",
    "        y_pre = []\n",
    "        for case in x_test:\n",
    "            y_pred = self.predict(case)   # return self.output_cells[:] ---> y_pred\n",
    "            for i in range(len(y_pred)):\n",
    "                if y_pred[i] == max(y_pred):\n",
    "                    y_pred[i] = 1\n",
    "                else:\n",
    "                    y_pred[i] = 0\n",
    "            y_pre.append(y_pred)\n",
    "        return inv_label_tr(np.array(y_pre))\n",
    "\n",
    "    \n",
    "    def fit2(self, x_test):  # 连续预测函数用于画图\n",
    "        y_pre = []\n",
    "        for case in x_test:\n",
    "            w = np.array([0, 1, 2])\n",
    "            y_pred = self.predict(case)\n",
    "            y_pre.append(np.array(y_pred).dot(w.T))\n",
    "        return np.array(y_pre)\n",
    "\n",
    "\n",
    "if __name__ == '__main__':  # 主函数\n",
    "    data = np.loadtxt('../../data/Iris.data', delimiter=',', converters={4: trtype})  # 读入数据，第五列转换为类别012\n",
    "    X, y = np.split(data, (4,), axis=1)  # 切分data和label\n",
    "    pca = PCA(n_components=2)\n",
    "    X = pca.fit_transform(X)  # 为方便绘图，对x进行PCA降维至二维\n",
    "    y = label_tr(y)\n",
    "    X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1, train_size=0.8)  # 划分数据\n",
    "    \n",
    "    nn = BPNeuralNetwork()\n",
    "    nn.setup(2, 5, 3)  # 初始化网络\n",
    "    nn.train(X_train, y_train, 10000, 0.1, 0.1)  # 训练\n",
    "    \n",
    "    y_pre_1d = nn.fit(X_test)  # 测试\n",
    "    y_test_1d = inv_label_tr(y_test)\n",
    "    print(f\"测试分数为: {accuracy_score(y_pre_1d, y_test_1d)}\")  # 打印测试分数\n",
    "    \n",
    "    # 画图\n",
    "    x_min, x_max = X_train[:, 0].min(), X_train[:, 0].max()  # 第0列的范围\n",
    "    y_min, y_max = X_train[:, 1].min(), X_train[:, 1].max()  # 第1列的范围\n",
    "    xx, yy = np.meshgrid(np.linspace(x_min, x_max, 200), np.linspace(y_min, y_max, 200))  # 生成网格采样点\n",
    "    grid_test = np.stack((xx.flat, yy.flat), axis=1)  # 测试点  （xx.flat降维）\n",
    "    y_predict = nn.fit2(grid_test)\n",
    "    cm_pt = mpl.colors.ListedColormap(['r', 'g', 'b'])  # 样本点颜色（样本分为3个类，三个颜色）\n",
    "    cm_bg = mpl.colors.ListedColormap(['b', 'y', 'gray'])  # 背景颜色\n",
    "    mpl.rcParams['font.sans-serif'] = [u'SimHei']   # 设置字体为SimHei显示中文\n",
    "    mpl.rcParams['axes.unicode_minus'] = False  # 设置正常显示字符\n",
    "    plt.figure(figsize=(7,5), dpi=100)\n",
    "    plt.xlim(x_min, x_max)\n",
    "    plt.ylim(y_min, y_max)  # 设置坐标范围\n",
    "    plt.pcolormesh(xx, yy, y_predict.reshape(xx.shape), shading='auto', cmap=cm_bg)  # 绘制网格背景\n",
    "    plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_pt, marker='o')  # 绘制样本点\n",
    "    plt.title(u'BPNN分类', fontsize=15)\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
